/*
 * nmats.c
 *
 *  Created on: Feb 25, 2009
 *  Author: Marco Luisi
 */

//riguardarsi gli include
//GESTIRE SIGPIPE!!!!!!!
#include "nmats.h"
#include "matrix.h" // basta nel .h?
#include "sysmacro.h" // da tnere?
#include <ctype.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <wait.h>


#define forever for(;;) // da spostare in un util.h da fare
#define N 10 //massimo numero utenti, vedere di non usarlo
#define MSG 32 // PID CONNECT user  user= 30 nel codice.. rivedere
#define WDIR nmat_data // rinominare
#define ONLINE 1
#define OFFLINE 0

//stioccare nel .h ?
void makedir(char*); // int per gli errori?
void init(); //tutte void?
void listen();
void spawn(char*, char*);//perchè non un array di 2 char*?
int validate(char*, user[]); // cambiare nome?ù //oppure fare in modo non serva il riferimento

FILE *fd;
//roba che serve nei sighandler..
volatile user *users; //vedere di togliere il limite
volatile int usersnum;
volatile worker *workers; //ha senso tenere un array? || possibile sezione critica per aggiornarlo?
volatile int active_workers;  // anche una lista ordinata ha poco senso?.. a che pro ordinare per pid?
volatile int workersnum; // possibile sezione critica per aggiornarlo?
//serve uno heap per memorizzarli?
int allocated;

char **aux;
char *buf;
char *string;
//char *msg; //cambiare in login_msg?
size_t len = 0;
//ssize_t read;
int u;
int i;
int pid;
char nmat_data[] = "WDIR"; // decidere che farne
DIR * wdir = NULL; // da nullificare?
//struct sigaction sg; // dentro il main o qui???

int server_pipe;
int server_pipe_aux; // dal libro

pid_t pid;
void chld_handler(int); //deve servire per la lista degli utenti
						// quando arriva un sigchld c'è da mettere il rispettivo utente offline
void int_handler(int);

void term_handler(int);
//fare array dimensionato con num utenti
//con gli fd delle rispettive pipe->utenti

// USARE STRDUP DOVE SI PUO'

// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//  ATTENZIONE [RISOLTO TOGLIENDO SLEEP??]
// SEGFAULT QUANDO DUE LOGIN IN SEQUENZA
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
int main(int argc, char **argv) {

	struct sigaction chld_action;
	bzero(&chld_action, sizeof(chld_action));
	chld_action.sa_handler = chld_handler;
	sigaction(SIGCHLD,&chld_action,NULL);

	struct sigaction int_action; //serve gestirlo o ignorarlo?
	bzero(&int_action, sizeof(int_action));
	int_action.sa_handler = int_handler;
	sigaction(SIGINT,&int_action,NULL);

	struct sigaction term_action;
	bzero(&term_action, sizeof(term_action));
	term_action.sa_handler = term_handler;
	sigaction(SIGTERM,&term_action,NULL);

    /* We don't want to block any other signals in this example */
    //sigemptyset(&chld_action.sa_mask);
    /* We're only interested in children that have terminated, not ones
     * which have been stopped (eg user pressing control-Z at terminal)
     */
    //chld_handler.sa_flags = SA_NOCLDSTOP;
    /*
     * Make these values effective. If we were writing a real
     * application, we would probably save the old value instead of
     * passing NULL.
     */
//    if (sigaction(SIGCHLD, &sg, NULL) < 0)
//    {
//        fprintf(stderr, "sigaction failed\n");
//        return 1;
//    }
	//fine gest segnali
	// da fare una cleanup() che toglie pattume in caso di precedente crash
	pid = getpid(); // non serve a una mazza?
	if (argc != 2) {
		WRITELN("NMAT Server: errore, specificare una lista di utenti!");
		exit(0); // guardare o 1?
	}

	fd = fopen(argv[1], "r"); //da input o fisso?
	if (fd == NULL) {
		WRITELN("NMAT Server: errore in lettura della lista utenti!");
		exit(EXIT_FAILURE); // brutto?
	}

	workersnum = 0;
	init();
	//daemon(1,1);
	listen();

	free(buf);
	free(string);
	//free(fd);
	free(users); // non vabene o si...?
	//opendir("..");
	printf("unlink:%d\n", unlink("../clientserver")); /* cancello clientserver se c'e gia' *///serve?
	perror("unlink:");

	WRITELN("NMAT Server in chiusura..");
	WRITELN("Arrivederci.");
	exit(0);
	return 0;
} // fine main


void init() { // lasciare void?
// FORSE FORSE ARRAY CON GLI FD delle pipe cs/sc da passare agli worker?
//	bzero(&sg, sizeof(sg)); //deprecato

	/* non fallisce se la pipe c’è gia’ */
	/* creo pipe con nome clientserver */

	printf("unlink:%d\n", unlink("./clientserver")); /* cancello clientserver se c'e gia' *///serve?
	perror("unlink:");
	IFERROR(mkfifo("clientserver",0666),"creazione del pipe con nome"); //rivedere la maschera
	/* apro la pipe clientserver in lettura */
	// ok rdwr?
	//si blocca fino al primo client, e spostarlo piu avanti?
	//sistemare con RW!!!
	IFERROR(server_pipe = open("clientserver",O_RDONLY ), "server: apro clientserver"); //O_NONBLOCK
	IFERROR(server_pipe_aux = open("clientserver",O_WRONLY), "server: AUX");//per evitare gli eof!!

	string = malloc(30 * sizeof(char)); // togliendo questa string da null.., provare ad aggiustare la dimensione
	usersnum = 0;
	// -1 a EOF a regola
	//se piu nomi su riga divisi da whitespace,prende solo il primo
	//ma le specifiche sono chiare..
	buf = malloc(30 * sizeof(char)); // funziona ma come sistemare queste dimensioni??
	allocated = 4; //poi mettere macro
	users = malloc(allocated * sizeof(user));

	active_workers = 0; // o nel main?
	while (fgets(buf, 30, fd) != NULL) { //ok questo modo di riallocare?
		if (usersnum == allocated) {			//ok riallocare per se?
			printf("realloc\n");
			//SEGNALI!!!
			allocated *= 2; //raddoppia la dimensione array
			users = (user*)realloc(users, allocated * sizeof(user)); // forse size of user*?
			if (users == NULL) { //da cambiare con la lista struct
				//fatal ("Virtual memory exhausted");
				printf("realloc fail\n");
				return; // SEGNALE?! //atensioòòn che init è void
			}

		}

		if (sscanf(buf, "%s", string) != -1) { //di suo scarta tutto scanf tranne \n
			//add user, farne funzione a se?
			user temp; // e la malloc? capire..
			temp.name = calloc(strlen(string) + 1, sizeof(char)); //rivedere +1
			temp.name = strdup(string);
			temp.status = 0;
			temp.wpid = 0; //forse questo parametro serve, ad esempio può essere killato uno worker .. o ppure no?

			printf("name %s status %d \n",temp.name, temp.status);
			users[usersnum] = temp;
			//fine add user
			printf("Utente %s abilitato.\n", users[usersnum].name);
			usersnum++; //forse era emeglio una var a parte per scorrere come era prima
			}
	}
	fclose(fd);
	printf("Abilitati %d utenti al server=%d\n", u, usersnum); //etico? e questo u?
	//users = realloc(users, usersnum * sizeof(user)); //
	for (i = 0; i < u; i++) { //riguardarsi sti controlli
		printf("%d:%s\n", i, users[i].name); // !! DA FARE CONTROLLO BOUNDS ARRAY !!!
	}

	// CREAZIONE DIRECTORY MAGARI FUNZIONE A PARTE
	//DECIDERE DOVE USARE SC E NO PER I FILE
	// da aprire la dir corrente?
	//struct dirent *ep; //rinominare e spostare
	wdir = NULL; //serve?
	wdir = opendir("nmat_data"); // imparare i permessi...
	perror("creazione directory:");
	if (wdir != NULL) {
		WRITELN("Directory di lavoro aperta.");
	} else {
		makedir("nmats_data"); // la macroooo
		WRITELN("Directory di lavoro creata.");
	}

	// creazione directory nmats
	if (chdir("nmats_data") == -1) { //capire perchè wdir definito senza " no nva
		perror("chdir");
		exit(errno);
	}
	//CREAzione directory degli utenti
	int x; // magari riusare i azzerandola
	for (x = 0; x < usersnum; x++) { // usare sempre u come riferimento per l'array è giusto?
		makedir(users[x].name); //cambiare la x
		//printf("Directory creata per l'Utente %s .\n", users[x]);
	}
	// CHIUDERE DIR E PIPES sopratutto al posto giusto
	// RICONTROLLARE LE FREE!!!!!!!!!!

}
//idea per la dim del pid.. calcolarlo in base alle
//cifre del pid del server..

/* Listen
 * nel caso ciclo infinito prova
 * ciclo lettura pipe
 * controllo msg-> nome
 * fork exec
 */

//vedere quali variabili servono fuori dallo scope di listen
void listen() { //cambiarne nome o inglobare nel main?
	//IN LINEA D MASSIMA SI PUO' usare
	// un singolo buffer per ricevere/inviare ?
	char *msg;
	msg = malloc(30 * sizeof(char));
	char *buffer;
	buffer = malloc(30 * sizeof(char));
	//msg = calloc(30, sizeof(char)); //rivedersi m/alloc
	int numread = 0; //spostare?
	int outc = 0; //out character
	int auth = 0; // 1 ok , 0 no, -1 giàlloggato
	int dim = 0; //provare senzaassegnare
	char *cpid; // da spostare?
	char *username; // perchè prende user.list?? prima d
	username = malloc(30 * sizeof(char));
	cpid = malloc(6 * sizeof(char));
	char *response; // capire bene come memorizzare queste var!!
	response = malloc(30 * sizeof(char));
	//int k = 0; // prima che strtok ci scriva su?
	char *o_pipe;
	o_pipe = malloc(30 * sizeof(char)); //spostare
	int out;
	WRITELN("inizio ciclo listen");
	while (1) { //FOREVAH
		numread = read(server_pipe, buffer, 30);
		printf("giunse !%s!\n", buffer);
		outc = snprintf(msg, (size_t)numread+1 , "%s", buffer); //da capire perchè serve +1

		if (numread > 0) {
			cpid = strtok(msg, " ");
			username = strtok(NULL, " "); //riuso username tanto per
			// controllo sul comando
			// pensare come gestire i comandi..
			// ma qui prima del fork prende solo connect..
			if (strcmp(username, "CONNECT") != 0) {//farne un literal?
				WRITELN("errore in connessione"); // o cmq rendere piu elegante il parsing
				return; // sistemare
			}
			username = strtok(NULL, " ");
			username =  strcat(username, "\0");
			//sprintf(username, "%s", strtok(NULL, " "));
			printf("validate di %s\n",username);
			sprintf(o_pipe,"%s%s","../serverclient",cpid);
			out = open(o_pipe, O_WRONLY);
			auth = validate(username, users);
			printf("dentro listen!\n");
			int aux;
			for(aux = 0; aux < usersnum; aux ++) {
				printf("utente %s , users[%d].status = %d\n", users[aux].name, aux, users[aux].status);
			}



			dim = sprintf(response,"%s%d","LOGIN ",auth); //il messaggio viene spedito a prescindere
			printf("invio dopo validate: %s\n", response);
			write(out, response, dim);//cambiare numread
			if (auth == 0) { // mettere in modo univoco significato 0/1
				// registro il pid dello worker assegnato al client
				spawn(cpid,username);
				//pause(); // con pause non accetta altre connessioni ovviamente
				printf("spawn di %s\n", cpid);
			}
			 // se 1 o -1 invia ma non succede altro
			else {
				WRITELN("utente non autenticato");
			}


		} else {
			WRITELN("letto niente"); // sto ramo se fatta bne la read non dovrebber servire..
		}
		//fflush(server_pipe);
		//username = NULL;

	}
	WRITELN("fine ciclo listen");
	//parse con strtok
}

//rifarla in modo serio
//per ora da -1
int validate (char *username, user u[]) { //o forse meglio il riferimento?
	int ret = 1; // na bella define per 0/1?
	int i = 0;
	//-1 gialloggato
	// -2 nome utente errato
	// > 0 ok.. l'indice nell'array
	//printf("validate di %s\n",username);
	//fare una ricerca decente,magari ricerca binaria
	//vedersi bene regole di scope
	while (ret && i < usersnum){  //sicuri di questo while?
		// o fare un for con un break?
		ret = strcmp(username, u[i].name); //strcmp da 0 per match, quindi..
		if (ret == 0) { //migliorare
			break;
		}
		i++;
	}
	if (ret == 0) { // trovato lo username
		printf("VALIDATE: status=%d\n", u[i].status);
		if (u[i].status){ //utente già connesso
			ret = -1;
			//qui la modifica
		}
		else { // utente ancora non connesso
			u[i].status = 1; //si può essere certi che se killato o per errore
							// uno worker questo valore rimanga consistente?
			printf("VALIDATE, DOPO AGGIORNAMENTO status = %d\n", users[i].status);
		}
	}
	return ret; // razionalizzare i return
}


void spawn(char* cpid, char* username) { //sembra non prendere l'username tra due chiamate vicine

	active_workers ++; // parte da zero

	workers = realloc(workers, active_workers * sizeof(worker)); //orrore
	worker temp;



	printf("Creazione del processo worker per l'utente %s:%s \n", username, cpid);
	pid = fork(); //controllo errori
	//perror("fork:");
	if (pid == 0) {  // etico fare qui il controllo?
		WRITELN("FIJO!!");// vedere se fare in modo che non serva il ..
		execl("../worker", "worker", username, cpid,(char*)NULL); //basta NULL?
			perror("exec");
			//WRITELN("sleep 2 secondi dopo exec");
			//users[index].wpid = pid;
			sleep(2);//perhè l'ho messa?
			return;
 }
	temp.wpid = pid;
	//temp.index = atoi(cpid);
	temp.user = calloc(strlen(username) + 1, sizeof(char)); //rivedere +1
	temp.user = strdup(username);
	workers[workersnum] = temp;
	workersnum++;//è inizializzato a 0 nel main

	WRITELN(" WORKER CREATO!"); //lo dice anche se non l ocrea

}





void makedir(char *dir_name) { //riferimento?
	//STABILIRE SE E' SYSCALL O NO!!!:D
	//DARE RIPULITA a questa funz
	if ((mkdir(dir_name, O_CREAT | 0666)) == -1) { // O_CREAT nonserve forse
		if (errno == EEXIST) {
			printf("Directory %s già presente.\n", dir_name);
			return; //magari bruttino?

		} else {
			perror("errore"); // capire questa errno sulla dir esistente..
			exit(errno);
		}

	}
	printf("Directory %s creata.\n", dir_name);
}


void chld_handler(int signum) {
//	int status, child_val;
//	/* Wait for any child without blocking */
//	    if (waitpid(-1, &status, WNOHANG) < 0)
//	    {
//	        /*
//	         * calling standard I/O functions like fprintf() in a
//	         * signal handler is not recommended, but probably OK
//	         * in toy programs like this one.
//	         */
//	        fprintf(stderr, "waitpid failed\n");
//	        return;
//	    }
//
//	    /*
//	     * We now have the info in 'status' and can manipulate it using
//	     * the macros in wait.h.
//	     */
//	    if (WIFEXITED(status))                /* did child exit normally? */
//	    {
//	        child_val = WEXITSTATUS(status); /* get child's exit status */
//	        printf("child's exited normally with status %d\n", child_val);
//	    }
	pid_t childpid;
	childpid = wait(NULL);
	printf("catturato sigchld da %d!\n",(int)childpid);
	int i;
	int y;

	for (i = 0; i< active_workers; i++) {
			printf("FOR su worker, ciclo %d\n", i);
		if(childpid == workers[i].wpid) {
			printf("trovato in worker[%d]:utente=%s,pid=%d\n",i,workers[i].user,workers[i].wpid);
			for (y = 0; y < usersnum; y++){
				printf("FOR su users, ciclo %d\n", y);
				if( strcmp(workers[i].user, users[y].name) == 0) {
					printf("USER[%d] name = %s status=%d\n",y , users[y].name,users[y].status);
					printf("workers[%d] per user=%s, wpid=%d\n", i, workers[i].user, workers[i].wpid);
					users[y].status = 0;

				}
			} // fine for sugli utenti
		}// fine for sugli workers


	}



}

void int_handler(int signum) {
	kill(0, SIGTERM);//cambiare!!
	//fare in modo che prima aspetti la fine di tutti gli worker!!
	while (pid = waitpid(-1, NULL, 0)) {
		printf("children!!!!\n");
		if (errno == ECHILD) {
			printf("MORTI TUTTI\n");
	      break;
	   }
	}
	exit(0);
}

void term_handler (int signum){ // o fare semplicemente che ritorna e la pulizia si
								// fa dal main?
	//SIG_IGN
	//sigaction(SIGCHLD,&chld_action,NULL);
	//chld_action.sa_handler = SIG_IGN;

	close(server_pipe);
	unlink("../clientserver");
	perror("nmats unlink serverclient");
	printf("Goodbye!\n");
	kill (0, SIGTERM); //rimandarsi term da soli mentre lo si gestisce?

	while ( waitpid(0,NULL,0) ) {
		printf("children!!!!\n");
		if (errno == ECHILD) {
			printf("MORTI TUTTI\n");
	      break;
	   }

	}

	exit(0);
}
